﻿using System;
using System.ComponentModel.Composition;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;
using DeepZoomPivotConstructor.VisualLib;
using DeepZoomPivotConstructor.VisualLib.Hotspots;
using RenderEngineInterfaces;

namespace DirectoryRender
{
    /// <summary>
    /// Given a string that is actually an existing file, attempt to render
    /// the existing file.
    /// </summary>
    [Export(typeof(IRenderItem))]
    [RenderInfo(typeof(FileWithTitle))]
    public class RenderFileWithTitle : IRenderItem, IRenderableObjectCompare
    {
        /// <summary>
        /// How much between slide boarders
        /// </summary>
        private double XBoarderSize { get; set; }
        /// <summary>
        /// How much between slide boarders
        /// </summary>
        private Double YBoarderSize { get; set; }

        /// <summary>
        /// The background color we should make the file...
        /// </summary>
        private Color FileBackgroundColor { get; set; }

        /// <summary>
        /// DPI to use when generating background with text - make it as small as possible
        /// to reduce memory
        /// </summary>
        private int DPIText { get; set; }

        /// <summary>
        /// How high should we make the text?
        /// </summary>
        private Double TextHeight { get; set; }

        public RenderFileWithTitle()
        {
            TextHeight = 0.5;
            YBoarderSize = 0.2;
            XBoarderSize = 0.2;
            DPIText = 150;
            FileBackgroundColor = Color.Gray;
        }

        /// <summary>
        /// So we can do a render or two...
        /// </summary>
        [Import(typeof(IRenderEngine))]
        private IRenderEngine RenderMe { get; set; }

        /// <summary>
        /// Do the rendering - we actually just create a file info and then get
        /// someone to try to render that. The "new" operator is fast enough that
        /// we don't need to take a hit on the effort of creating a task.
        /// [We also know the path exists as a file on the OS as we've checked
        /// it below - if that changes out from under us... well... :-)]
        /// </summary>
        /// <param name="what"></param>
        /// <returns></returns>
        public Task<Visual> Render(object what)
        {
            return Task<Visual>.Factory.StartNew(() => RunTheRender(what));
        }

        /// <summary>
        /// If we should be using a different temp directory than the one that is there by default... :-)
        /// </summary>
        [Import("RenderingTempDir", AllowDefault = true)]
#pragma warning disable 0649
        private DirectoryInfo RenderingTempDir;
#pragma warning restore 0649

        /// <summary>
        /// Do the actual render - has to be done as a future,
        /// even though we are going to be doing very little.
        /// </summary>
        /// <param name="what"></param>
        /// <returns></returns>
        private Visual RunTheRender(object what)
        {
            ///
            /// Render the actual file
            /// 

            FileWithTitle fspec = what as FileWithTitle;
            Visual fv = RenderMe.Render(fspec.FileSpec);

            if (fv == null)
            {
                return null;
            }

            ///
            /// Rescale it and prep it for being in front of a title
            /// 

            fv.ScaleToWidth(10.0);
            fv.IncZIndex();

            ///
            /// Next, the title and basic meta-data
            /// 

            VisualTitleAndBackground title = new VisualTitleAndBackground(fv)
            {
                {TextHeight, fspec.FileSpec.Name, new HSActionLink(new Uri("file://" + fspec.FileSpec.FullName))}
            };
            if (RenderingTempDir != null)
            {
                title.ImageWritingDirectory = RenderingTempDir;
            }

            title.Info = new Visual.VisualInfo()
            {
                Title = fspec.FileSpec.Name
            };

            title.Info = fv.Info;

            ///
            /// Finally, mark this guy as a compositional unit so
            /// that we don't have to always re-render it!
            /// 

            title.SetAsComposableUnit(fspec.FileSpec.LastWriteTime);

            return title;
        }

        /// <summary>
        /// If this what is a string that is a valid path pointing to a real file, then
        /// we can go for it! But only generically. :-)
        /// </summary>
        /// <param name="what"></param>
        /// <returns></returns>
        public RenderPluginPriority RenderPriority(object what)
        {
            FileWithTitle t = what as FileWithTitle;
            if (t != null && t.FileSpec.Exists)
            {
                return RenderPluginPriority.RPPGoodChoice;
            }
            return RenderPluginPriority.RPPCantDoIt;
        }

        /// <summary>
        /// Return a hash of the full file name.
        /// </summary>
        /// <param name="what"></param>
        /// <returns></returns>
        public long GetObjectHash(object what)
        {
            return (what as FileWithTitle).FileSpec.FullName.GetHashCode();
        }

        /// <summary>
        /// Compare the two files full path names for equality.
        /// </summary>
        /// <param name="o1"></param>
        /// <param name="o2"></param>
        /// <returns></returns>
        public bool ObjectsEqual(object o1, object o2)
        {
            FileWithTitle f1 = o1 as FileWithTitle;
            FileWithTitle f2 = o2 as FileWithTitle;

            return f1.FileSpec.FullName == f2.FileSpec.FullName;
        }

        /// <summary>
        /// Check to see if the date has been changed!
        /// </summary>
        /// <param name="what"></param>
        /// <param name="metaData"></param>
        /// <returns></returns>
        public bool IsOutOfDate(object what, object metaData)
        {
            FileWithTitle f = what as FileWithTitle;
            DateTime oldModTime = (DateTime)metaData;

            return f.FileSpec.LastWriteTime > oldModTime;
        }

        /// <summary>
        /// Get the date of the object for the meta-data.
        /// </summary>
        /// <param name="what"></param>
        /// <returns></returns>
        public object GetObjectDateMetaData(object what)
        {
            FileWithTitle f = what as FileWithTitle;
            return f.FileSpec.LastWriteTime;
        }
    }
}
